#include "basic.h"
#include "connections.h"
#include "List/simclist_impl.h"
#include "io.h"

extern char* BOOT_IP;

in_addr_t start_boot(in_addr_t *my_ip,in_addr_t *exitCode/*-1, oppure ip del SP*/, int *conn_number) {
	int *socksd = enstablish_tcp_connection(BOOT_IP, BOOT_WELCOME_PORT);
	if (socksd == -1) {
		printf("Ritento connessione tra 5 secondi\n");
		sleep(5);
		*conn_number = *conn_number + 1;
		if (*conn_number > 4)
			exit(0);
		return start_boot(my_ip, exitCode, conn_number);
	}

	int is_superpeer = -1;
	int get_list = 0;
	size_t lunghezza_lista;

	if (*exitCode == -1) {

		get_list = 0;
		//boot semplice
		if (write(*socksd, &get_list, sizeof(int)) < 0) {
			printf("Errore nella connessione al bootstrap write problem\n");
			printf("Ritento connessione tra 5 secondi\n");
			sleep(5);
			*conn_number = *conn_number + 1;
			if (*conn_number > 4)
				exit(0);
			return start_boot(my_ip, exitCode, conn_number);
		}
	} else {
		get_list = -1;
		if (write(*socksd, &get_list, sizeof(int)) < 0) {
			printf("Errore nella connessione al bootstrap write problem\n");
			printf("Ritento connessione tra 5 secondi\n");
			sleep(5);
			*conn_number = *conn_number + 1;
			if (*conn_number > 4)
				exit(0);
			return start_boot(my_ip, exitCode, conn_number);
		}
		if (write(*socksd, exitCode, sizeof(in_addr_t)) < 0) {
			printf("Errore nella connessione al bootstrap write problem\n");
			printf("Ritento connessione tra 5 secondi\n");
			sleep(5);
			*conn_number = *conn_number + 1;
			if (*conn_number > 4)
				exit(0);
			return start_boot(my_ip, exitCode, conn_number);
		}

	}

	if (read(*socksd, &is_superpeer, sizeof(int)) < 0) {
		printf("Errore nella connessione al bootstrap read problem\n");
		printf("Ritento connessione tra 5 secondi\n");
		sleep(5);
		*conn_number = *conn_number + 1;
		if (*conn_number > 4)
			exit(0);
		return start_boot(my_ip, exitCode, conn_number);
	}

	// PRENDO IL MIO IP
	struct sockaddr_in local;
	in_addr_t ip;

	if (read(*socksd, &ip, sizeof(in_addr_t)) < 0) {
		printf("Errore nella connessione al bootstrap read problem\n");
		printf("Ritento connessione tra 5 secondi\n");
		*conn_number = *conn_number + 1;
		if (*conn_number > 4)
			exit(0);
		return start_boot(my_ip, exitCode, conn_number);
	}

	*my_ip = ip;
	local.sin_addr.s_addr = ip;

	struct in_addr myIP = local.sin_addr;
	printf("dal boot My IP: %s\n", inet_ntoa(myIP));

	if (is_superpeer == 0) {
		printf("ATTENDO LISTA\n");
		if (read(*socksd, &lunghezza_lista, sizeof(size_t)) < 0) {
			printf("Errore nella connessione al bootstrap read problem\n");
			printf("Ritento connessione tra 5 secondi\n");
			sleep(5);
			*conn_number = *conn_number + 1;
			if (*conn_number > 4)
				exit(0);
			return start_boot(my_ip, exitCode, conn_number);
		}

		printf("LISTA = %zu BYTE\n", lunghezza_lista);
		int n;
		void *mem = malloc(1000);
		int file_list = open("TEMP_BOOT_LIST", O_RDWR | O_CREAT, 0666);
		while (lunghezza_lista > 0 && ((n = read(*socksd, mem, 1000)) > 0)) {
			if ((write(file_list, mem, n)) < 0) { // invio a blocchi di 1000 byte
				printf("ERRORE nella connessione al bootstrap in write\n");
				sleep(5);
				*conn_number = *conn_number + 1;
				if (*conn_number > 4)
					exit(0);
				return start_boot(my_ip, exitCode, conn_number);
			}
			printf("RICEVUTI %d BYTE\n", n);
			memset(mem, 0, 1000);
			lunghezza_lista -= n;
		}
		free(mem);
		close(*socksd);
		free(socksd);
	} else {
		printf("ELETTO A SUPERPEER\n");
	}
	return is_superpeer;
}

list_t *get_superpeer_list() {

	list_t *superpeer_list = malloc(sizeof(list_t));
	list_init(superpeer_list);

	int *socksd = enstablish_tcp_connection(BOOT_IP, BOOT_WELCOME_PORT);

	if (socksd == -1) {
		return NULL;
	}



	size_t lunghezza_lista;
	int get_list = 1;



	if (write(*socksd, &get_list, sizeof(int)) < 0) {
		printf("Errore in get_superpeer_list write\n");
		superpeer_list = NULL;
		return superpeer_list;
	}

	printf("ATTENDO LISTA\n");
	if (read(*socksd, &lunghezza_lista, sizeof(size_t)) < 0) {
		printf("Errore in get_superpeer_list read\n");
		superpeer_list = NULL;
		return superpeer_list;
	}

	printf("LISTA = %zu BYTE\n", lunghezza_lista);
	int n;
	void *mem = malloc(1000);
	int file_list = open("TEMP_SUPERPEER_LIST", O_RDWR | O_CREAT, 0666);
	while (lunghezza_lista > 0 && ((n = read(*socksd, mem, 1000)) > 0)) {
		if ((write(file_list, mem, n)) < 0) { // invio a blocchi di 1000 byte
			printf("Errore in get_superpeer_list write\n");
		}
		printf("RICEVUTI %d BYTE\n", n);
		memset(mem, 0, 1000);
		lunghezza_lista -= n;
	}
	free(mem);
	if (lunghezza_lista > 0) {
		printf("Errore lista non ricevuta completamente\n");
		superpeer_list = NULL;
		return superpeer_list;
	}
	if (list_restore_file(superpeer_list, "TEMP_SUPERPEER_LIST",
			&lunghezza_lista) < 0) {
		perror("Errore list_restore_file su temp superpeer list\n");
		superpeer_list = NULL;
		return superpeer_list;
	}

	printf("%s", "RICEVUTA LISTA E FATTO RESTORE\n");

	if (remove("TEMP_SUPERPEER_LIST") < 0) {
		printf("Errore in get_superpeer_list remove");
	}
	if (close(*socksd) < 0) {
		printf("Errore in close in boot methods\n");
		superpeer_list = NULL;
		return superpeer_list;
	}
	free(socksd);

	return superpeer_list; // va fatto free dopo averla usata
}

void get_superpeer_list_thread() {

	list_t superpeer_list;
	list_init(&superpeer_list);

	int *socksd = enstablish_tcp_connection(BOOT_IP, BOOT_WELCOME_PORT);
	size_t lunghezza_lista;
	int get_list = 1;

	if (write(*socksd, &get_list, sizeof(int)) < 0) {
		printf("Errore in get_superpeer_list write\n");
		return;
	}

	printf("ATTENDO LISTA\n");
	if (read(*socksd, &lunghezza_lista, sizeof(size_t)) < 0) {
		printf("Errore in get_superpeer_list read\n");
		return;
	}

	printf("LISTA = %zu BYTE\n", lunghezza_lista);
	int n;
	void *mem = malloc(1000);
	int file_list = open("TEMP_SUPERPEER_LIST", O_RDWR | O_CREAT, 0666);
	while (lunghezza_lista > 0 && ((n = read(*socksd, mem, 1000)) > 0)) {
		if ((write(file_list, mem, n)) < 0) { // invio a blocchi di 1000 byte
			printf("Errore in get_superpeer_list write\n");
		}
		printf("RICEVUTI %d BYTE\n", n);
		memset(mem, 0, 1000);
		lunghezza_lista -= n;
	}
	free(mem);
	if (lunghezza_lista > 0) {
		printf("Errore lista non ricevuta completamente\n");

		return;
	}
	if (list_restore_file(&superpeer_list, "TEMP_SUPERPEER_LIST",
			&lunghezza_lista) < 0) {
		perror("Errore list_restore_file su temp superpeer list\n");

		return;
	}

	printf("%s", "RICEVUTA LISTA E FATTO RESTORE\n");

	if (remove("TEMP_SUPERPEER_LIST") < 0) {
		printf("Errore in get_superpeer_list remove");
	}
	if (close(*socksd) < 0) {
		printf("Errore in close in boot methods\n");

		return;
	}
	free(socksd);

	print_superpeer_list(&superpeer_list);

	return;
}

in_addr_t choose_superpeer(int by_ping) { // by_ping=1 sceglie il più vicino, by_ping=0 il primo della lista
	list_t *ping_list = NULL;
	list_t superpeer_list;
	list_init(&superpeer_list);
	size_t lunghezza_lista;

	if (list_restore_file(&superpeer_list, "TEMP_BOOT_LIST", &lunghezza_lista)
			< 0) {
		printf("lunghezza %d", lunghezza_lista);
		perror("ERRORE IN list_restore_file temp boot list");
		exit(0);
	}

	if (remove("TEMP_BOOT_LIST") < 0) {
		perror("ERRORE IN remove file");
		exit(0);
	}

	if (by_ping == 1) {
		ping_list = list_sort_by_ping(&superpeer_list);
		elem_ip_ping_t *elem = (elem_ip_ping_t *) list_get_at(ping_list, 0);
		return elem->ip;
	}

	else {
		elem_superpeer_t *elem = (elem_superpeer_t *) list_get_at(
				&superpeer_list, 0);
		return elem->ip;
	}
}

int init_directory() {
	int fd;
	char dir[60];
	if ((fd = open("USER_IMP", O_RDWR, 0666)) < 0) {
		printf("INSERIRE DIRECTORY DI LAVORO\n");
		while (fgets(dir, 60, stdin) < 0) {
		}

		char make_path[70] = { 0 }; // P2P
		strncpy(make_path, dir, strlen(dir) - 1);
		strcat(make_path, "/P2P");
		if (mkdir(make_path, 0777) < 0)
			return -1;

		char make_path2[70] = { 0 };
		strncpy(make_path2, dir, strlen(dir) - 1);
		strcat(make_path2, "/P2P/Ricevuti"); // RICEVUTI
		if (mkdir(make_path2, 0777) < 0)
			return -1;

		char make_path3[70] = { 0 };
		strncpy(make_path3, dir, strlen(dir) - 1);
		strcat(make_path3, "/P2P/Condivisi"); // CONDIVISI
		if (mkdir(make_path3, 0777) < 0)
			return -1;

		int user = open("USER_IMP", O_CREAT | O_RDWR, 0666);

		if (write(user, dir, strlen(dir) - 1) < 0) {
			fprintf(stderr, "errore in write nome");
			exit(1);
		}
		close(user);

	}
	return 0;
}

void check_server_ip() {
	char *new_ip = malloc(20);
	char path[90] = { 0 };
	int dir_file = open("USER_IMP", O_RDONLY, 0666);
	read(dir_file, path, 40);
	close(dir_file);
	strcat(path, "/server.met");
	int fd;
	if ((fd = open("server.met", O_RDWR|O_CREAT, 0666)) < 0) {
		printf("lista server non aperta\n");
		exit(1);
	}
	int n = read(fd, new_ip, 18);
	if (n < 0) {
		printf("errore in read\n");
		exit(0);
	}
	if (n == 0) {
		free(new_ip);
		return;
	}
	printf("----------IP SERVER CONFIGURATO DALL'UTENTE---------- %s \n",new_ip);
	BOOT_IP = new_ip;

	return;
}



